package org.example.pglog.util;

import org.example.pglog.pojo.BeanDiff;
import org.example.pglog.pojo.FieldDiff;
import org.example.pglog.pojo.User;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author: 船长
 * @create: 2024-09-01 19:36
 **/

public class BeanCompareStringUtil {
    private static final int FILTER_TYPE_INDEX = 0;
    private static final int FILTER_MAP_INDEX = 1;

    // 存放过滤类型和字段与中文名的映射
    private static ThreadLocal<List<Object>> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {

        User oldUser = new User("1", "张三", 20);
        User newUser = new User("2", "李四", 21);

        String[] fieldArray = new String[]{"age", "name", "hobby"};
        String[] fieldNameArray = new String[]{"年龄", "姓名", "爱好"};

        BeanDiff beanDiff = compareInclude(oldUser, newUser, fieldArray, fieldNameArray);

        List<FieldDiff> list = beanDiff.getFieldDiffList();
        if (list != null && list.size() > 0) {
            for (FieldDiff fieldDiff : list) {
                System.out.println(fieldDiff.toString());
            }
        }
    }

    /**
     * bean比较
     * @param oldBean
     * @param newBean
     * @return
     */
    public static BeanDiff compare(Object oldBean, Object newBean) {
        BeanDiff beanDiff = new BeanDiff();

        Class<?> oldClass = oldBean.getClass();
        Class<?> newClass = newBean.getClass();

        if (oldClass.equals(newClass)) {
            List<Field> fieldList = new ArrayList<>();
            fieldList = getCompareFieldList(fieldList, newClass);

            List<Object> list = threadLocal.get();
            @SuppressWarnings("unchecked")
            Map<String, String> fieldMap = (Map<String, String>) list.get(FILTER_MAP_INDEX);

            boolean needInclude = false;
            boolean needExclude = false;

            if (list != null && fieldMap != null && !fieldMap.isEmpty()) {
                String type = (String) list.get(FILTER_TYPE_INDEX);

                if ("INCLUDE".equals(type)) {
                    needInclude = true;
                } else if ("EXCLUDE".equals(type)) {
                    needExclude = true;
                }
            }

            for (Field field : fieldList) {
                field.setAccessible(true);

                try {
                    Object oldValue = field.get(oldBean);
                    Object newValue = field.get(newBean);

                    String fieldName = field.getName();

                    if (fieldMap.containsKey(fieldName)) {
                        if (nullableNotEquals(oldValue, newValue)) {
                            FieldDiff fieldDiff = new FieldDiff(fieldName, fieldMap.get(fieldName), oldValue, newValue);
                            beanDiff.addFieldDiff(fieldDiff);
                        }
                    } else {
                        // 处理 EXCLUDE 逻辑：排除在外的字段不需要进行比较
                        if (needInclude) continue;
                    }

                } catch (IllegalArgumentException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        return beanDiff;
    }

    /**
     * bean比较
     * @param oldBean
     * @param newBean
     * @param includeFieldArray 需要包含的字段
     * @param fieldNameArray    包含字段的中文名称数组
     * @return
     */
    public static BeanDiff compareInclude(Object oldBean, Object newBean, String[] includeFieldArray, String[] fieldNameArray) {
        Map<String, String> fieldMap = createFieldMap(includeFieldArray, fieldNameArray);

        List<Object> list = new ArrayList<>();
        list.add("INCLUDE");
        list.add(fieldMap);
        threadLocal.set(list);

        return compare(oldBean, newBean);
    }

    /**
     * bean比较
     * @param oldBean
     * @param newBean
     * @param excludeFieldArray 需要排除的字段
     * @param fieldNameArray    排除字段的中文名称数组
     * @return
     */
    public static BeanDiff compareExclude(Object oldBean, Object newBean, String[] excludeFieldArray, String[] fieldNameArray) {
        Map<String, String> fieldMap = createFieldMap(excludeFieldArray, fieldNameArray);

        List<Object> list = new ArrayList<>();
        list.add("EXCLUDE");
        list.add(fieldMap);
        threadLocal.set(list);

        return compare(oldBean, newBean);
    }

    /**
     * 创建字段名与中文名映射
     * @param fieldArray
     * @param fieldNameArray
     * @return
     */
    private static Map<String, String> createFieldMap(String[] fieldArray, String[] fieldNameArray) {
        Map<String, String> fieldMap = new HashMap<>();
        for (int i = 0; i < fieldArray.length; i++) {
            fieldMap.put(fieldArray[i], fieldNameArray[i]);
        }
        return fieldMap;
    }

    /**
     * 获取需要比较的字段list
     * @param fieldList
     * @param clazz
     * @return
     */
    private static List<Field> getCompareFieldList(List<Field> fieldList, Class<?> clazz) {
        Field[] fieldArray = clazz.getDeclaredFields();

        fieldList.addAll(Arrays.asList(fieldArray));

        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            getCompareFieldList(fieldList, superClass);
        }
        return fieldList;
    }

    /**
     * 比较值是否不相等
     * @param oldValue
     * @param newValue
     * @return
     */
    private static boolean nullableNotEquals(Object oldValue, Object newValue) {

        if (oldValue == null && newValue == null) {
            return false;
        }

        if (oldValue != null && oldValue.equals(newValue)) {
            return false;
        }

        return !("".equals(oldValue) && newValue == null) && !("".equals(newValue) && oldValue == null);
    }

    /**
     * 复制源对象中的字段值到目标对象中，如果字段名匹配的话。
     *
     * @param targetObject   目标对象，字段值将被复制到这个对象中。
     * @param sourceObject   源对象，字段值将从这个对象中获取。
     * @param fieldName      字段名称，用于匹配字段。
     * @param <T>            对象的类型。
     * @return               更新后的目标对象。
     */
    public static <T> T copyFieldValue(T targetObject, T sourceObject, String fieldName) {
        Field[] fields = sourceObject.getClass().getDeclaredFields();

        for (Field field : fields) {
            // 设置字段可访问
            field.setAccessible(true);
            // 检查字段名是否匹配
            if (field.getName().equals(fieldName)) {
                try {
                    // 从源对象获取字段值
                    Object value = field.get(sourceObject);
                    // 将值设置到目标对象中
                    field.set(targetObject, value);
                    System.out.println("字段名称: " + fieldName + ", 值: " + value);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return targetObject;
    }
}
